Handling Errors
Occasionally, a system software routine might be unable to perform the service you've requested of it. You might, for instance, passGetResource
a resource specification that doesn't apply to any resource in any of the open resource files. Or, the user might have opened so many document windows that there simply isn't enough space in your application's heap to open another one. In these situations, you need to determine that an error has occurred and react to it in some appropriate manner.The system software has several ways of informing your application that a requested service is not possible. Many functions return a result code that indicates whether the function completed successfully, and if not, what the reason for failure was. These functions return a result of type
OSErr
. Here's an example:
myResult := FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, myVRefNum, myDirID); IF myResult = noErr THEN ... ELSE ...;Other routines--mainly procedures and functions that return other types of results--don't return a result code directly. To find out whether these kinds of routines were successful, you need to call an additional system software routine. For example, some Resource Manager procedures don't directly indicate if the resource operation was successful or not. To find that out, you can call theResError
function. TheDoSavePrefs
routine (defined in Listing 3-6 on page 66) uses this strategy to update a preferences resource:
RmveResource(myHandle); IF ResError = noErr THEN AddResource(myPrefData, kPrefResType, kPrefResID, myName); IF ResError = noErr THEN WriteResource(myPrefData);Similarly, the Resource Manager routineGet1Resource
returns a handle to the specified resource data. If for some reason the resource cannot be opened, the function returns a handle whose value isNIL
. You can inspect the returned value to determine whether it's safe to proceed.
myHandle := Get1Resource(kPrefResType, kPrefResID); IF myHandle <> NIL THEN ...;You could also callResError
to determine ifGet1Resource
succeeded. In other words, the following lines are equivalent to the preceding ones:
myHandle := Get1Resource(kPrefResType, kPrefResID); IF ResError <> noErr THEN ...;The Memory Manager provides theMemError
function, which works much asResError
does. For Memory Manager functions that return a value, you can either inspect the returned value or callMemError
to determine if the function completed successfully.This book has used a fairly simple strategy for detecting and reacting to the normal kinds of problems. When calling a function that returns a pointer or handle, Venn Diagrammer checks that the value of that pointer or handle isn't
NIL
. If it isNIL
, Venn Diagrammer usually just skips any code that uses that pointer or handle.
Occasionally, an application might run into some more serious problem during its execution that renders further processing impossible or undesirable. For example, if the Venn Diagrammer application isn't able to allocate enough memory for the data structure it uses to maintain information about a document window's geometry, there's no point in continuing to run, because the application won't be able to draw anything in any document windows. In that case, the application should gracefully terminate its own execution. (See Listing 5-3 on page 95.)
- IMPORTANT
- Venn Diagrammer's error-handling strategy is far too simple for most applications, and it runs afoul of good human interface principles. For example, if the
DoCreateWindow
function (defined in Listing 6-6 on page 117) cannot allocate the memory it needs, it exits and returns aNIL
window pointer to the calling routine. The net result is that no new window is created, in spite of the user's desire to create one. At the very least,DoCreateWindow
should inform the user that a new window could not be created because sufficient memory was not available.![]()
To do this, the Venn Diagrammer application defines the
DoBadError
procedure and calls it whenever there is a problem serious enough to warrant such drastic action. TheDoBadError
procedure is defined in Listing 9-5.Listing 9-5 Handling serious errors
PROCEDURE DoBadError (myError: Integer); VAR myItem: Integer; myMessage: Str255; BEGIN SetCursor(arrow); {set arrow cursor} GetIndString(myMessage, kErrorStrings, myError); ParamText(myMessage, '', '', ''); myItem := Alert(rErrorAlert, NIL); {display message} ExitToShell; {terminate execution} END;The application passesDoBadError
an index into a resource of type'STR#'
that contains messages indicating the types of serious errors. FirstDoBadError
sets the cursor to the standard arrow cursor (this step is necessary only if your application ever changes the cursor). ThenDoBadError
retrieves the appropriate message from the application's resource fork and calls the Dialog Manager routineParamText
to substitute the message into the alert box text. After that,DoBadError
displays the alert box by calling the Dialog Manager routineAlert
. (See Figure 7-2 on page 134 for an example of this alert box.) Finally,DoBadError
calls the Process Manager procedureExitToShell
to terminate the application immediately.